
# include "dfxAudioSource_OAL.h"
# include "dfxLog.h"

namespace DFX
{
    AudioSource_OAL::AudioSource_OAL()
        : mNumBuffers(0), mLooping(false)
    {
        alGenSources(1, &mSourceID);

        alSourcef(mSourceID, AL_GAIN, 1.0f);
        alSourcei(mSourceID, AL_LOOPING, 0);
    }

    AudioSource_OAL::~AudioSource_OAL()
    {
        Stop();

        alDeleteSources(1, &mSourceID);
        alDeleteBuffers(1, mBufferID);
    }

    bool AudioSource_OAL::IsPlaying() const
    {
        ALint state;
        alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);

        return (state == AL_PLAYING);
    }

    void AudioSource_OAL::Play()
    {
        if (IsPlaying())
            return;

        if (!mWaveDataProvider)
            return;

        ALint state;
        alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);

        if (state != AL_PAUSED && mWaveDataProvider->IsStreaming())
        {
            UnqueueAll();

            StreamBuffer(mBufferID[0], BUFFER_SIZE);
            StreamBuffer(mBufferID[1], BUFFER_SIZE);

            alSourceQueueBuffers(mSourceID, 2, mBufferID);
        }

        alSourcePlay(mSourceID);
    }

    void AudioSource_OAL::Stop()
    {
        alSourceStop(mSourceID);
    }
    void AudioSource_OAL::Pause()
    {
        alSourcePause(mSourceID);
        UnqueueAll();
    }

    void AudioSource_OAL::LoopSound(bool loop)
    {
        mLooping = loop;

        if (mWaveDataProvider && mWaveDataProvider->IsStreaming())
            return;

        alSourcei(mSourceID, AL_LOOPING, loop ? 1 : 0);
    }
    void AudioSource_OAL::SetVolume(Float32 volume)
    {
        alSourcef(mSourceID, AL_GAIN, volume);
    }

    void AudioSource_OAL::Update(Float32 deltaSeconds)
    {
        if (!mWaveDataProvider || !IsPlaying())
            return;

        if (mWaveDataProvider->IsStreaming())
        {
            ALint processed;
            alGetSourcei(mSourceID, AL_PROCESSED, &processed);

            while (processed--)
            {
                ALuint buffId;
                alSourceUnqueueBuffers(mSourceID, 1, &buffId);

                StreamBuffer(buffId, BUFFER_SIZE);

                if (mWaveDataProvider->IsEOF())
                {
                    if (mLooping)                    
                        mWaveDataProvider->Seek(0);
                    
                    StreamBuffer(buffId, BUFFER_SIZE);
                }

                alSourceQueueBuffers(mSourceID, 1, &buffId);
            }
        }
    }

    void AudioSource_OAL::BindWaveForm(WaveDataProvider *wave)
    {
        mWaveDataProvider = wave;

        if (!mWaveDataProvider)
            return;

        if (mWaveDataProvider->IsStreaming())
        {
            mNumBuffers = 2;
            alGenBuffers(2, mBufferID);
        }
        else
        {
            ALenum alFormat = AL_FORMAT_MONO8;

            switch (mWaveDataProvider->GetWaveFormat())
            {
            case WAVE_FORMAT_MONO8:
                alFormat = AL_FORMAT_MONO8;
                break;

            case WAVE_FORMAT_MONO16:
                alFormat = AL_FORMAT_MONO16;
                break;

            case WAVE_FORMAT_STEREO8:
                alFormat = AL_FORMAT_STEREO8;
                break;

            case WAVE_FORMAT_STEREO16:
                alFormat = AL_FORMAT_STEREO16;
                break;
            }

            mNumBuffers = 1;           

            alGenBuffers(mNumBuffers, &mBufferID[0]);
            alBufferData(mBufferID[0],
                alFormat,
                mWaveDataProvider->GetWaveData(),
                (ALsizei)mWaveDataProvider->GetWaveDataSize(),
                (ALsizei)mWaveDataProvider->GetSamplePerSec());

            alSourcei(mSourceID, AL_BUFFER, mBufferID[0]);
        }
    }

    void AudioSource_OAL::UnqueueAll()
    {
        ALint queued;
        alGetSourcei(mSourceID, AL_BUFFERS_QUEUED, &queued);

        if (queued > 0)
            alSourceUnqueueBuffers(mSourceID, queued, mBufferID);
    }

    ALint AudioSource_OAL::StreamBuffer(ALuint bufferId, ALint size)
    {
        ALint actualSize = (ALint)mWaveDataProvider->StreamWaveData(size);
        ALenum alFormat = AL_FORMAT_MONO8;

        switch (mWaveDataProvider->GetWaveFormat())
        {
        case WAVE_FORMAT_MONO8:
            alFormat = AL_FORMAT_MONO8;
            break;

        case WAVE_FORMAT_MONO16:
            alFormat = AL_FORMAT_MONO16;
            break;

        case WAVE_FORMAT_STEREO8:
            alFormat = AL_FORMAT_STEREO8;
            break;

        case WAVE_FORMAT_STEREO16:
            alFormat = AL_FORMAT_STEREO16;
            break;
        }

        alBufferData(bufferId,
            alFormat,
            mWaveDataProvider->GetWaveData(),
            (ALsizei)mWaveDataProvider->GetWaveDataSize(),
            (ALsizei)mWaveDataProvider->GetSamplePerSec());

        return actualSize;
    }
}